// Copyright 2007-2008 Rory Plaire (codekaizen@gmail.com)

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using NUnit.Framework;
using AffineMatrixF = NPack.AffineMatrix<NPack.SingleComponent>;
using VectorF = NPack.Vector<NPack.SingleComponent>;
using IVectorF = NPack.Interfaces.IVector<NPack.SingleComponent>;

namespace NPack.Tests.DirectX
{
    [TestFixture]
    public class DXAffineTransformTests
    {
        private static readonly Double _e = 0.00005;

        [Test]
        public void RotationTest()
        {
            Matrix m1 = Matrix.CreateRotationX((Single)Math.PI / 4);

            AffineMatrixF a1 = new AffineMatrixF(MatrixFormat.RowMajor, 4);
            a1.RotateAlong(new VectorF(1, 0, 0), Math.PI / 4);

            Vector4[] dxPoints = new Vector4[]
                {
                    new Vector4(1, 0, 0, 1), 
                    new Vector4(0, 1, 0, 1), 
                    new Vector4(0, 0, 1, 1)
                };

            Vector4[] output = new Vector4[3];

            Vector4.Transform(dxPoints, ref m1, output);

            VectorF[] npackPoints = new VectorF[]
                {
                    new VectorF(1, 0, 0, 1), 
                    new VectorF(0, 1, 0, 1), 
                    new VectorF(0, 0, 1, 1),
                };
            List<IVectorF> npackResults = new List<IVectorF>();
            npackResults.AddRange(a1.TransformVectors(npackPoints));

            Assert.AreEqual(m1.M11, (Single)a1[0, 0], _e);
            Assert.AreEqual(m1.M12, (Single)a1[0, 1], _e);
            Assert.AreEqual(m1.M13, (Single)a1[0, 2], _e);
            Assert.AreEqual(m1.M14, (Single)a1[0, 3], _e);

            Assert.AreEqual(m1.M21, (Single)a1[1, 0], _e);
            Assert.AreEqual(m1.M22, (Single)a1[1, 1], _e);
            Assert.AreEqual(m1.M23, (Single)a1[1, 2], _e);
            Assert.AreEqual(m1.M24, (Single)a1[1, 3], _e);

            Assert.AreEqual(m1.M31, (Single)a1[2, 0], _e);
            Assert.AreEqual(m1.M32, (Single)a1[2, 1], _e);
            Assert.AreEqual(m1.M33, (Single)a1[2, 2], _e);
            Assert.AreEqual(m1.M34, (Single)a1[2, 3], _e);

            Assert.AreEqual(m1.M41, (Single)a1[3, 0], _e);
            Assert.AreEqual(m1.M42, (Single)a1[3, 1], _e);
            Assert.AreEqual(m1.M43, (Single)a1[3, 2], _e);
            Assert.AreEqual(m1.M44, (Single)a1[3, 3], _e);

            Assert.AreEqual(output[0].X, (Single)npackResults[0][0], _e);
            Assert.AreEqual(output[0].Y, (Single)npackResults[0][1], _e);
            Assert.AreEqual(output[0].Z, (Single)npackResults[0][2], _e);
            Assert.AreEqual(output[1].X, (Single)npackResults[1][0], _e);
            Assert.AreEqual(output[1].Y, (Single)npackResults[1][1], _e);
            Assert.AreEqual(output[1].Z, (Single)npackResults[1][2], _e);
            Assert.AreEqual(output[2].X, (Single)npackResults[2][0], _e);
            Assert.AreEqual(output[2].Y, (Single)npackResults[2][1], _e);
            Assert.AreEqual(output[2].Z, (Single)npackResults[2][2], _e);
        }

        [Test]
        public void TranslateTest()
        {
            Matrix m1 = Matrix.CreateTranslation(new Vector3(5, -10, 3));

            AffineMatrixF a1 = new AffineMatrixF(MatrixFormat.RowMajor, 4);
            a1.Translate(new VectorF(5, -10, 3));

            Vector4[] dxPoints = new Vector4[]
                {
                    new Vector4(1, 0, 0, 1), 
                    new Vector4(0, 1, 0, 1), 
                    new Vector4(0, 0, 1, 1)
                };

            Vector4[] output = new Vector4[3];

            Vector4.Transform(dxPoints, ref m1, output);

            VectorF[] npackPoints = new VectorF[]
                {
                    new VectorF(1, 0, 0, 1), 
                    new VectorF(0, 1, 0, 1), 
                    new VectorF(0, 0, 1, 1),
                };

            List<IVectorF> npackResults = new List<IVectorF>();
            npackResults.AddRange(a1.TransformVectors(npackPoints));

            Assert.AreEqual(m1.M11, (Single)a1[0, 0], _e);
            Assert.AreEqual(m1.M12, (Single)a1[0, 1], _e);
            Assert.AreEqual(m1.M13, (Single)a1[0, 2], _e);
            Assert.AreEqual(m1.M14, (Single)a1[0, 3], _e);

            Assert.AreEqual(m1.M21, (Single)a1[1, 0], _e);
            Assert.AreEqual(m1.M22, (Single)a1[1, 1], _e);
            Assert.AreEqual(m1.M23, (Single)a1[1, 2], _e);
            Assert.AreEqual(m1.M24, (Single)a1[1, 3], _e);

            Assert.AreEqual(m1.M31, (Single)a1[2, 0], _e);
            Assert.AreEqual(m1.M32, (Single)a1[2, 1], _e);
            Assert.AreEqual(m1.M33, (Single)a1[2, 2], _e);
            Assert.AreEqual(m1.M34, (Single)a1[2, 3], _e);

            Assert.AreEqual(m1.M41, (Single)a1[3, 0], _e);
            Assert.AreEqual(m1.M42, (Single)a1[3, 1], _e);
            Assert.AreEqual(m1.M43, (Single)a1[3, 2], _e);
            Assert.AreEqual(m1.M44, (Single)a1[3, 3], _e);

            Assert.AreEqual(output[0].X, (Single)npackResults[0][0], _e);
            Assert.AreEqual(output[0].Y, (Single)npackResults[0][1], _e);
            Assert.AreEqual(output[0].Z, (Single)npackResults[0][2], _e);
            Assert.AreEqual(output[1].X, (Single)npackResults[1][0], _e);
            Assert.AreEqual(output[1].Y, (Single)npackResults[1][1], _e);
            Assert.AreEqual(output[1].Z, (Single)npackResults[1][2], _e);
            Assert.AreEqual(output[2].X, (Single)npackResults[2][0], _e);
            Assert.AreEqual(output[2].Y, (Single)npackResults[2][1], _e);
            Assert.AreEqual(output[2].Z, (Single)npackResults[2][2], _e);
        }

        [Test]
        public void ScaleTest()
        {
            Matrix m1 = Matrix.CreateScale(new Vector3(100, -100, 33));

            AffineMatrixF a1 = new AffineMatrixF(MatrixFormat.RowMajor, 4);
            a1.Scale(new VectorF(100, -100, 33));

            Vector4[] dxPoints = new Vector4[]
                {
                    new Vector4(1, 0, 0, 1), 
                    new Vector4(0, 1, 0, 1), 
                    new Vector4(0, 0, 1, 1)
                };

            Vector4[] output = new Vector4[3];

            Vector4.Transform(dxPoints, ref m1, output);

            VectorF[] npackPoints = new VectorF[]
                {
                    new VectorF(1, 0, 0, 1), 
                    new VectorF(0, 1, 0, 1), 
                    new VectorF(0, 0, 1, 1),
                };
            List<IVectorF> npackResults = new List<IVectorF>();
            npackResults.AddRange(a1.TransformVectors(npackPoints));

            Assert.AreEqual(m1.M11, (Single)a1[0, 0], _e);
            Assert.AreEqual(m1.M12, (Single)a1[1, 0], _e);
            Assert.AreEqual(m1.M13, (Single)a1[2, 0], _e);
            Assert.AreEqual(m1.M14, (Single)a1[3, 0], _e);

            Assert.AreEqual(m1.M21, (Single)a1[0, 1], _e);
            Assert.AreEqual(m1.M22, (Single)a1[1, 1], _e);
            Assert.AreEqual(m1.M23, (Single)a1[2, 1], _e);
            Assert.AreEqual(m1.M24, (Single)a1[3, 1], _e);

            Assert.AreEqual(m1.M31, (Single)a1[0, 2], _e);
            Assert.AreEqual(m1.M32, (Single)a1[1, 2], _e);
            Assert.AreEqual(m1.M33, (Single)a1[2, 2], _e);
            Assert.AreEqual(m1.M34, (Single)a1[3, 2], _e);

            Assert.AreEqual(m1.M41, (Single)a1[0, 3], _e);
            Assert.AreEqual(m1.M42, (Single)a1[1, 3], _e);
            Assert.AreEqual(m1.M43, (Single)a1[2, 3], _e);
            Assert.AreEqual(m1.M44, (Single)a1[3, 3], _e);

            Assert.AreEqual(output[0].X, (Single)npackResults[0][0], _e);
            Assert.AreEqual(output[0].Y, (Single)npackResults[0][1], _e);
            Assert.AreEqual(output[0].Z, (Single)npackResults[0][2], _e);
            Assert.AreEqual(output[1].X, (Single)npackResults[1][0], _e);
            Assert.AreEqual(output[1].Y, (Single)npackResults[1][1], _e);
            Assert.AreEqual(output[1].Z, (Single)npackResults[1][2], _e);
            Assert.AreEqual(output[2].X, (Single)npackResults[2][0], _e);
            Assert.AreEqual(output[2].Y, (Single)npackResults[2][1], _e);
            Assert.AreEqual(output[2].Z, (Single)npackResults[2][2], _e);
        }

        [Test]
        public void RotateAtTest()
        {
            Matrix t = Matrix.CreateTranslation(new Vector3(10, 0, 0));
            Matrix r = Matrix.CreateRotationZ((Single)Math.PI);
            Matrix tInv = Matrix.Invert(t);
            Matrix m1 = tInv * r * t;

            AffineMatrixF a1 = new AffineMatrixF(MatrixFormat.RowMajor, 4);
            a1.RotateAt(new VectorF(10, 0, 0), new VectorF(0, 0, 1), Math.PI);

            Vector4[] dxPoints = new Vector4[]
                {
                    new Vector4(1, 0, 0, 1), 
                    new Vector4(0, 1, 0, 1), 
                    new Vector4(0, 0, 1, 1)
                };

            Vector4[] output = new Vector4[3];

            Vector4.Transform(dxPoints, ref m1, output);

            VectorF[] npackPoints = new VectorF[]
                {
                    new VectorF(1, 0, 0, 1), 
                    new VectorF(0, 1, 0, 1), 
                    new VectorF(0, 0, 1, 1),
                };
            List<IVectorF> npackResults = new List<IVectorF>();
            npackResults.AddRange(a1.TransformVectors(npackPoints));

            Assert.AreEqual(m1.M11, (Single)a1[0, 0], _e);
            Assert.AreEqual(m1.M12, (Single)a1[0, 1], _e);
            Assert.AreEqual(m1.M13, (Single)a1[0, 2], _e);
            Assert.AreEqual(m1.M14, (Single)a1[0, 3], _e);

            Assert.AreEqual(m1.M21, (Single)a1[1, 0], _e);
            Assert.AreEqual(m1.M22, (Single)a1[1, 1], _e);
            Assert.AreEqual(m1.M23, (Single)a1[1, 2], _e);
            Assert.AreEqual(m1.M24, (Single)a1[1, 3], _e);

            Assert.AreEqual(m1.M31, (Single)a1[2, 0], _e);
            Assert.AreEqual(m1.M32, (Single)a1[2, 1], _e);
            Assert.AreEqual(m1.M33, (Single)a1[2, 2], _e);
            Assert.AreEqual(m1.M34, (Single)a1[2, 3], _e);

            Assert.AreEqual(m1.M41, (Single)a1[3, 0], _e);
            Assert.AreEqual(m1.M42, (Single)a1[3, 1], _e);
            Assert.AreEqual(m1.M43, (Single)a1[3, 2], _e);
            Assert.AreEqual(m1.M44, (Single)a1[3, 3], _e);

            Assert.AreEqual(output[0].X, (Single)npackResults[0][0], _e);
            Assert.AreEqual(output[0].Y, (Single)npackResults[0][1], _e);
            Assert.AreEqual(output[0].Z, (Single)npackResults[0][2], _e);
            Assert.AreEqual(output[1].X, (Single)npackResults[1][0], _e);
            Assert.AreEqual(output[1].Y, (Single)npackResults[1][1], _e);
            Assert.AreEqual(output[1].Z, (Single)npackResults[1][2], _e);
            Assert.AreEqual(output[2].X, (Single)npackResults[2][0], _e);
            Assert.AreEqual(output[2].Y, (Single)npackResults[2][1], _e);
            Assert.AreEqual(output[2].Z, (Single)npackResults[2][2], _e);
        }

        [Test]
        [Ignore]
        public void ShearTest()
        {
        }

        [Test]
        public void CombinedTransformsTest()
        {
            Matrix r = Matrix.CreateRotationX((Single) Math.PI/4);
            Matrix t = Matrix.CreateTranslation(new Vector3(1, 2, -3));
            Matrix s = Matrix.CreateScale(new Vector3(-1, 3, 5));
            Matrix m1 = r * t * s;

            AffineMatrixF a1 = new AffineMatrixF(MatrixFormat.RowMajor, 4);
            a1.RotateAlong(new VectorF(1, 0, 0), Math.PI / 4);
            a1.Translate(new VectorF(1, 2, -3));
            a1.Scale(new VectorF(-1, 3, 5));

            Assert.AreEqual(m1.M11, (Single)a1[0, 0], _e);
            Assert.AreEqual(m1.M12, (Single)a1[0, 1], _e);
            Assert.AreEqual(m1.M13, (Single)a1[0, 2], _e);
            Assert.AreEqual(m1.M14, (Single)a1[0, 3], _e);

            Assert.AreEqual(m1.M21, (Single)a1[1, 0], _e);
            Assert.AreEqual(m1.M22, (Single)a1[1, 1], _e);
            Assert.AreEqual(m1.M23, (Single)a1[1, 2], _e);
            Assert.AreEqual(m1.M24, (Single)a1[1, 3], _e);

            Assert.AreEqual(m1.M31, (Single)a1[2, 0], _e);
            Assert.AreEqual(m1.M32, (Single)a1[2, 1], _e);
            Assert.AreEqual(m1.M33, (Single)a1[2, 2], _e);
            Assert.AreEqual(m1.M34, (Single)a1[2, 3], _e);

            Assert.AreEqual(m1.M41, (Single)a1[3, 0], _e);
            Assert.AreEqual(m1.M42, (Single)a1[3, 1], _e);
            Assert.AreEqual(m1.M43, (Single)a1[3, 2], _e);
            Assert.AreEqual(m1.M44, (Single)a1[3, 3], _e);

            Vector4[] dxPoints = new Vector4[]
                {
                    new Vector4(1, 0, 0, 1), 
                    new Vector4(0, 1, 0, 1), 
                    new Vector4(0, 0, 1, 1)
                };

            Vector4[] output = new Vector4[3];

            Vector4.Transform(dxPoints, ref m1, output);

            VectorF[] npackPoints = new VectorF[]
                {
                    new VectorF(1, 0, 0, 1), 
                    new VectorF(0, 1, 0, 1), 
                    new VectorF(0, 0, 1, 1),
                };
            List<IVectorF> npackResults = new List<IVectorF>();
            npackResults.AddRange(a1.TransformVectors(npackPoints));

            Assert.AreEqual(output[0].X, (Single)npackResults[0][0], _e);
            Assert.AreEqual(output[0].Y, (Single)npackResults[0][1], _e);
            Assert.AreEqual(output[0].Z, (Single)npackResults[0][2], _e);
            Assert.AreEqual(output[1].X, (Single)npackResults[1][0], _e);
            Assert.AreEqual(output[1].Y, (Single)npackResults[1][1], _e);
            Assert.AreEqual(output[1].Z, (Single)npackResults[1][2], _e);
            Assert.AreEqual(output[2].X, (Single)npackResults[2][0], _e);
            Assert.AreEqual(output[2].Y, (Single)npackResults[2][1], _e);
            Assert.AreEqual(output[2].Z, (Single)npackResults[2][2], _e);
        }
    }
}
